package com.iflytek.jcy.supervise.clue.component.permission;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.iflytek.jcy.supervise.clue.common.pojo.CommonResponse;
/**
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2022/1/12
 */
@Configuration
// @EnableWebMvc    // SpringBoot (≥2.6.x)需要额外添加
public class SaTokenConfigure implements WebMvcConfigurer {
    protected final Logger logger = LoggerFactory.getLogger(SaTokenConfigure.class);

    private final String[] excludePath = {"/swagger-ui.html", "/webjars/**", "/v2/api-docs", "/swagger-resources/**",
            "/doc.html", "/v3/api-docs", "/error", "/favicon.ico", "/index/login","/index/loginout","/index/user","/clue/dept/ry","/clue/dept/list", 
            "/model/all-enable","/clue/check/**", "/model/full/**"};

    @Value("${sa-token.swagger.verify:false}")
    private boolean swaggerVerify;

    @Value("${sa-token.swagger.user:admin}")
    private String swaggerUsername;
    @Value("${sa-token.swagger.password:Admin!@123}")
    private String swaggerPassword;

    @Bean
    @ConditionalOnExpression("'${sa-token.jwt}'.equals('true')")
    public StpLogic getStpLogicJwt() {
        // 集成jwt, token通过header传递, 可以不用走cookie
        return new StpLogicJwtForSimple();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册注解拦截器，并排除不需要注解鉴权的接口地址 (与登录拦截器无关)
        registry.addInterceptor(new SaInterceptor(handler -> {
            // 登录认证 -- 拦截所有路由，并排除/user/doLogin 用于开放登录
            SaRouter.match("/**").notMatch(excludePath).notMatchMethod(SaHttpMethod.OPTIONS.name()).check(r -> {
                StpUtil.checkLogin();
            });

            // 角色认证 -- 拦截以 admin 开头的路由，必须具备 admin 角色或者 super-admin 角色才可以通过认证
            // SaRouter.match("/admin/**", r -> StpUtil.checkRoleOr("admin", "super-admin"));

            // 权限认证 -- 不同模块认证不同权限
            // SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
            // SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));

            // 甚至你可以随意的写一个打印语句
            // SaRouter.match("/**", r -> System.out.println("----啦啦啦1----"));

            // 连缀写法
            // SaRouter.match("/**").check(r -> System.out.println("----啦啦啦2----"));

            // 多个条件一起使用
            // SaRouter.match(SaHttpMethod.GET).match("/user/**", "/goods/**", "/art/get/{id}")
            //         .notMatch("/**/*.js").notMatch("/**/*.css")
            //         .check(r -> System.out.println("----啦啦啦3----"));
        })).addPathPatterns("/**");
    }

    /*
    既然拦截器已经可以实现路由鉴权，为什么还要用过滤器再实现一遍呢？简而言之：
    1. 相比于拦截器，过滤器更加底层，执行时机更靠前，有利于防渗透扫描
    2. 过滤器可以拦截静态资源，方便我们做一些权限控制
    3. 部分Web框架根本就没有提供拦截器功能，但几乎所有的Web框架都会提供过滤器机制

    但是过滤器也有一些缺点，比如：
    1. 由于太过底层，导致无法率先拿到HandlerMethod对象，无法据此添加一些额外功能
    2. 由于拦截的太全面了，导致我们需要对很多特殊路由(如/favicon.ico)做一些额外处理
    3. 在Spring中，过滤器中抛出的异常无法进入全局@ExceptionHandler，我们必须额外编写代码进行异常处理

    */

    @Bean
    /**
     * 注册 [Sa-Token全局过滤器]
     */
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()
                // 拦截与排除 path
                .addInclude("/**").addExclude("/favicon.ico")

                // 全局认证函数
                .setAuth(obj -> {
                    // ...
                })

                // 异常处理函数
                .setError(e -> {
                    return CommonResponse.failed(e.getMessage());
                })

                // 前置函数：在每次认证函数之前执行
                .setBeforeAuth(obj -> {
                    // StpUtil.login(10001);

                    // ---------- 设置跨域响应头 ----------
                    SaHolder.getResponse()
                            // 允许指定域访问跨域资源
                            .setHeader("Access-Control-Allow-Origin", "*")
                            // 允许所有请求方式
                            .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
                            // 有效时间
                            .setHeader("Access-Control-Max-Age", "3600")
                            // 允许的header参数
                            .setHeader("Access-Control-Allow-Headers", "*");

                    // 如果是预检请求，则立即返回到前端
                    SaRouter.match(SaHttpMethod.OPTIONS).free(r -> {}).back();
                });
    }
}
